#include <CType.h>
#include <ASMLIB.h>

static uint32_t iumul16(uint16_t x, uint16_t y);

void ldiv(int32_t x, int32_t y, div_t * pDiv)
{
    int32_t t = 0, r = 0;
    int16_t i;
    int16_t f = 0;

    if (x < 0)
        f = ~f, x = -x;

    if (y < 0)
        f = ~f, y = -y;

    for (i = 30; i >= 0; i--)
        {
        r = lushl(r, 1);
        t = lushl(t, 1);

        t = t | (lushr(x, i) & 1);
        if (t - y >= 0)
            {
            r = r | 1;
            t = t - y;
            }
        }

    pDiv->quot = r;
    pDiv->rem = t;
}

static uint32_t iumul16(uint16_t x, uint16_t y)
{
    uint16_t rh, rl;
    uint32_t r;
    asm push ax;
    asm push bx;
    asm push cx;
    asm mov ax, x;
    asm mov bx, y;
    asm mul bx;
    asm mov rh, dx;
    asm mov rl, ax;
    asm pop cx;
    asm pop bx;
    asm pop ax;
    r = ((uint32_t)rh) << 16 | rl;

    return r;
}

void lmul(int32_t x, int32_t y, mul_t * pMul)
{
    uint16_t xh, xl, yh, yl, rh, rl;
    uint32_t t, r, s, rt;
    int16_t f = 0, adc = 0;

    if (x < 0)
        f = ~f, x = -x;

    if (y < 0)
        f = ~f, y = -y;

    t = x;
    r = y;

    xh = (t >> 16) & 0xFFFF;
    xl = t & 0xFFFF;

    yh = (r >> 16) & 0xFFFF;
    yl = r & 0xFFFF;

    /* AB*CD = BD + (AD+BC) << 16 + AC << 32 */

    t = iumul16(xl, yl); /* BD */
    s = iumul16(xh, yh); /* AC */

    r = iumul16(xh, yl); /* AD */

    rt = iumul16(xl, yh); /* BC */

    if (rt + r < r) /* check overflow */
        adc++;

    r = r + rt; /* BC+AD */

    rh = (r >> 16) & 0xFFFF;
    rl = r & 0xFFFF;

    rt = ((uint32_t)rl) << 16;

    if (rt + t < t)
        adc++;

    t = t + rt;

    s = s + rh + adc;

    if (f)
        pMul->high = -s;
    else
        pMul->high = s;

    pMul->low = t;

    return;
}


int32_t lushl(int32_t x, int8_t c)
{
    uint16_t p1, p2;
    int16_t i;
    p1 = x & 0xFFFF;
    p2 = (x >> 16) & 0xFFFF;

    for(i = 0; i < c; i++)
        {
        p2 = p2 << 1;
        if (p1 & 0x8000)
            {
            p2 = p2 | 1;
            }
        p1 = p1 << 1;
        }

    x = p1 | (((uint32_t)p2) << 16);
    return x;
}

int32_t lushr(int32_t x, int8_t c)
{
    uint16_t p1, p2;
    int16_t i;
    p1 = x & 0xFFFF;
    p2 = (x >> 16) & 0xFFFF;

    for(i = 0; i < c; i++)
        {
        p1 = p1 >> 1;
        if (p2 & 1)
            {
            p1 = p1 | 0x8000;
            }
        p2 = p2 >> 1;
        }

    x = p1 | (((uint32_t)p2) << 16);
    return x;
}

int16_t getIP()
{
    int16_t ip_r;

    asm call _GetIpCall;
    asm _GetIpCall: mov ax,ax;
    asm pop ax;
    asm mov ip_r, ax;
    return ip_r;
}

int16_t getCS()
{
    int16_t cs_r;

    asm mov cs_r, cs;

    return cs_r;
}
